//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation

Module Name:

commain.cpp

Abstract:


Notes:


--*/


#include "precomp.h"

#ifdef UNDER_CE
#include <pegdser.h>
#endif // UNDER_CE


//
// Global init data used by COM_OpenInternal()
//
static DWORD g_dwComInitData = 0;


//
// COM driver initialization
//
DWORD COM_Init(DWORD dwInfo)
{
    FUNCTION_TRACE(COM_Init(virt));
    RETAILMSG(MSG_ON, (TEXT("RILDrv : t : COM_Init : params: dwInfo = %d\r\n"), dwInfo));

    CComHandle* pDevice = NULL;
    DWORD dwRet = NULL;

    // Allocate a device handle
    pDevice = new CComHandle;
    if (!pDevice || !pDevice->Init()) {
        goto Error;
    }

    dwRet = (DWORD)pDevice;
    g_dwComInitData = dwRet;
    RETAILMSG(MSG_ON, (TEXT("RILDrv : t : COM_Init : Started successfully\r\n")));

Error:
    if (!dwRet) {
        delete pDevice;
    }
    return dwRet;
}


//
// COM driver de-initialization
//
BOOL COM_Deinit(DWORD dwData)
{
    FUNCTION_TRACE(COM_Deinit(virt));
    DEBUGCHK(dwData != 0);

    CComHandle* pDevice = (CComHandle*)dwData;
    BOOL fRet = FALSE;

    if (!pDevice) {
        goto Error;
    }

    delete pDevice;
    fRet = TRUE;

Error:
    return fRet;
}


//
// Open a COM driver instance
//
DWORD COM_Open(DWORD dwData, DWORD dwAccess, DWORD dwShareMode)
{
    FUNCTION_TRACE(COM_Open(virt));
    DEBUGCHK(dwData != 0);

    CComInstanceHandle* pHandle = NULL;
    CComHandle* pDevice = (CComHandle*)dwData;
    DWORD dwRet = 0;

    if (!pDevice) {
        goto Error;
    }

    pHandle = new CComInstanceHandle;
    if (!pHandle || !pHandle->Init(pDevice, dwAccess)) {
        goto Error;
    }

    dwRet = (DWORD)pHandle;

Error:
    if (!dwRet) {
        delete pHandle;
    }
    return dwRet;
}


//
// Close a COM driver instance
//
BOOL COM_Close(DWORD dwOpenData)
{
    FUNCTION_TRACE(COM_Close(virt));
    DEBUGCHK(0 != dwOpenData);

    CComInstanceHandle* pHandle = NULL;
    CComHandle* pDevice = NULL;
    BOOL fRet = FALSE;

    pHandle = (CComInstanceHandle*)dwOpenData;
    if (!pHandle) {
        goto Error;
    }

    delete pHandle;
    fRet = TRUE;

Error:
    return fRet;
}


//
// COM read
//
DWORD COM_Read(DWORD dwOpenData, LPVOID pBuf, DWORD len)
{
//    FUNCTION_TRACE(COM_Read(virt));
    DEBUGCHK(0 != dwOpenData);

    CComHandle* pDevice = NULL;
    CBuffer* pBuffer = NULL;
    UINT cbCopied;
    DWORD dwDownstreamRead = 0;
    DWORD dwRet = 0;

    if (!ValidateOpenData(dwOpenData, pDevice)) {
        goto Error;
    }

    if (len > 0) {
        if (pDevice->EnterSharedUse())
        {
            cbCopied = pDevice->ReadFromBackupBuffer(pBuf, len);
            if (len > cbCopied && !pDevice->Read((BYTE*)pBuf + cbCopied, len - cbCopied, dwDownstreamRead)) {
                dwRet = (DWORD)-1;
            }
            else {
                dwRet = cbCopied + dwDownstreamRead;
                pDevice->UpdateComStatData(TRUE, dwRet);
            }

            (void)pDevice->ExitSharedUse();
        }
        else
        {
            dwRet = (DWORD)-1;
        }
    }

Error:
    return dwRet;
}


//
// COM write
//
DWORD COM_Write(DWORD dwOpenData, LPCVOID pBuf, DWORD len)
{
//    FUNCTION_TRACE(COM_Write(virt));
    DEBUGCHK(dwOpenData != 0);

    CComHandle* pDevice = NULL;
    DWORD dwRet = 0;

    if (!ValidateOpenData(dwOpenData, pDevice)) {
        goto Error;
    }

    if (pDevice->EnterSharedUse())
    {
        if (pDevice->Write(pBuf, len, dwRet)) {
            pDevice->UpdateComStatData(FALSE, dwRet);
        } else {
            dwRet = (DWORD)-1;
        }

        (void)pDevice->ExitSharedUse();
    }
    else
    {
        dwRet = (DWORD)-1;
    }

Error:
    return dwRet;
}


//
// COM seek
//
DWORD COM_Seek(DWORD dwOpenData, long pos, DWORD type)
{
    return (DWORD)-1;
}




//
// COM IO-control
//
BOOL COM_IOControl(DWORD dwOpenData, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
                   PDWORD pdwActualOut)
{
//    FUNCTION_TRACE(COM_IOControl(virt));
    DEBUGCHK(0 != dwOpenData);

    CComHandle* pDevice = NULL;
    DWORD dwOutUsed;
    BOOL fRet = FALSE;

    bool fKeepWaiting = false;

    if (!ValidateOpenData(dwOpenData, pDevice)) {
        goto Error;
    }

    do
    {
#ifndef DEDICATED_DATA_PORT
        if (dwCode == IOCTL_SERIAL_WAIT_ON_MASK)
        {
            pDevice->ResetDataModeInterrupted();
            fKeepWaiting = true;
        }
#endif

        if (pDevice->EnterSharedUse())
        {

#ifdef UNDER_CE
            // Don't let the outside users change DTR
            if (IOCTL_SERIAL_SET_DTR == dwCode || IOCTL_SERIAL_CLR_DTR == dwCode) {
                fRet = TRUE;
                (void)pDevice->ExitSharedUse();
                goto Error;
            }
#endif // UNDER_CE

            BOOL fOk = FALSE;
            __try
            {
                fOk = pDevice->IOControl(dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, dwOutUsed);
            }
            __except( EXCEPTION_EXECUTE_HANDLER )
            {
                fOk = FALSE;
            }
            if ( !fOk )
            {
                (void)pDevice->ExitSharedUse();
                goto Error;
            }

#ifndef DEDICATED_DATA_PORT
            if (dwCode == IOCTL_SERIAL_WAIT_ON_MASK)
            {
                // Check  if a command was sent while we were asleep.
                // If not, then we weren't woken up erroneously, and we can return.
                // otherwise we should wait for the next comm event.
                if (!pDevice->FDataModeInterrupted())
                {
                    fKeepWaiting = false;
                }
            }
#endif

            (void)pDevice->ExitSharedUse();
        }
        else
        {
            goto Error;
        }
    }while (fKeepWaiting);

    if (pdwActualOut)
    {
        fRet = CeSafeCopyMemory( pdwActualOut, &dwOutUsed, sizeof(DWORD) );
    }
    else
    {
        fRet = TRUE;
    }

Error:
    return fRet;
}



//
// Used by RIL to open virtual serial port
//
CComHandle* COM_OpenInternal()
{
    FUNCTION_TRACE(COM_OpenInternal);

    CComHandle* pRet = NULL;
    CComHandle* pDevice;

#ifdef DEDICATED_DATA_PORT

    // Allocate a device handle
    pDevice = new CComHandle;
    if (!pDevice || !pDevice->Init()) {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : COM_Init failed when called from COM_OpenInternal\r\n")));
        goto Error;
    }

    g_dwComInitData = (DWORD)pDevice;

#endif

    DEBUGCHK(g_dwComInitData != 0);

    pDevice = (CComHandle*)g_dwComInitData;
    if (!pDevice) {
        goto Error;
    }

    if (!pDevice->OpenDownstreamPort()) {
        goto Error;
    }
    pRet = pDevice;

Error:
    return pRet;
}


//
// Ued by RIL to close virtual serial port
//
void COM_CloseInternal(CComHandle* const pDevice)
{
    FUNCTION_TRACE(COM_CloseInternal);
    DEBUGCHK(pDevice != NULL);

    (void)pDevice->CloseDownstreamPort();
}


//
//
//
BOOL ValidateOpenData(const DWORD dwOpenData, CComHandle*& rpDevice)
{
    DEBUGCHK(dwOpenData != 0);

    CComInstanceHandle* pHandle = NULL;
    BOOL fRet = FALSE;

    pHandle = (CComInstanceHandle*)dwOpenData;
    if (!pHandle) {
        goto Error;
    }

    rpDevice = pHandle->GetDevice();
    DEBUGCHK(rpDevice != NULL);

    if (!rpDevice->FHandleIsOwner(pHandle)) {
        goto Error;
    }
    fRet = TRUE;

Error:
    return fRet;
}


